﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.Timers;
using System.Speech.Recognition;

namespace Sara
{
    class SpeechRecognizer
    {
        private SpeechRecognitionEngine speechRecognitionEngine;
        private Window1 window;

        private SpeechSynthesis speechSynthesis;

        private DispatcherTimer sleepTimer;

        private Timer acknowledgementTimer;

        private bool isSaraAsleep = true;

        private const int timeout = 60;

        DateTime lastActivity = DateTime.Now;

        DateTime alarmTime = DateTime.Now;

        bool alarmSet = false;

        DateTime snoozeTime = DateTime.Now;

        bool snoozeActive = false;


        public SpeechRecognizer(Window1 window)
        {
            this.window = window;
        }

        public void initialize()
        {
            sleepTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Normal, new EventHandler(monitorSara), Dispatcher.CurrentDispatcher);
            sleepTimer.Start();

            acknowledgementTimer = new Timer();

            window.textBlock1.Text = "Loading...";

            RecognizerInfo recognizer = null;
            foreach (RecognizerInfo info in SpeechRecognitionEngine.InstalledRecognizers())
            {
                if (info.Culture.Name.Equals("en-US"))
                {
                    recognizer = info;
                    break;
                }
            }

            if (recognizer == null)
            {
                throw new NotSupportedException("United States English speech engine is not installed.");
            }

            speechRecognitionEngine = new SpeechRecognitionEngine(recognizer);

            speechRecognitionEngine.AudioLevelUpdated += 
                new EventHandler<AudioLevelUpdatedEventArgs>(SpeechEngine_AudioLevelUpdated);
            speechRecognitionEngine.SpeechDetected +=
                new EventHandler<SpeechDetectedEventArgs>(SpeechEngine_SpeechDetected);
            speechRecognitionEngine.SpeechHypothesized += 
                new EventHandler<SpeechHypothesizedEventArgs>(SpeechEngine_SpeechHypothesized);
            speechRecognitionEngine.SpeechRecognized += 
                new EventHandler<SpeechRecognizedEventArgs>(SpeechEngine_SpeechRecognized);
            speechRecognitionEngine.SpeechRecognitionRejected += 
                new EventHandler<SpeechRecognitionRejectedEventArgs>(SpeechEngine_SpeechRecognitionRejected);
            speechRecognitionEngine.RecognizeCompleted += 
                new EventHandler<RecognizeCompletedEventArgs>(SpeechEngine_RecognizeCompleted);

            speechRecognitionEngine.LoadGrammar(SaraGrammarBuilder(System.Globalization.CultureInfo.CreateSpecificCulture("en-US")));

            speechRecognitionEngine.SetInputToDefaultAudioDevice();

            speechRecognitionEngine.RecognizeAsync(RecognizeMode.Multiple);

            //RecognitionResult result = speechRecognitionEngine.Recognize(TimeSpan.FromSeconds(20));
            //window.textBlock1.Text = result.Text + " (" + result.Confidence + ")";
            //window.textBlock1.Text = "Listening...";
            window.textBlock1.Text = "Ready";
            speechSynthesis = new SpeechSynthesis(window);
        }

        void SpeechEngine_SpeechDetected(object sender, SpeechDetectedEventArgs e)
        {
            
        }

        void SpeechEngine_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            //window.textBlock1.Text = "Hypothesized: \"" + e.Result.Text + "\"(" + e.Result.Confidence + ")\n";
            acknowledgementTimer.Stop();
        }

        public void SpeechEngine_AudioLevelUpdated(object sender, AudioLevelUpdatedEventArgs e)
        {
        }

        public void SpeechEngine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            //window.textBlock1.Text = "Recognized: \"" + e.Result.Text + "\"(" + e.Result.Confidence + ")\n";

            if (e.Result.Semantics.Count > 0) // has children
            {
                parseSpeech(new KeyValuePair<string, SemanticValue>("root", e.Result.Semantics));
            }
        }

        public void parseSpeech(KeyValuePair<string, SemanticValue> semantic)
        {
            // handle the top level semantic
            string key = semantic.Key;
            SemanticValue value = semantic.Value;

            if (isSaraAsleep && !key.Equals("directAddress") && !key.Equals("root"))
            {
                return;
            }

            if (!isSaraAsleep || key.Equals("directAddress"))
            {
                wakeSara();
            }

            switch (key)
            {
                case "root":
                    break;

                case "directAddress":
                    window.textBlock1.Text = "";
                    acknowledgementTimer = new Timer(250);
                    acknowledgementTimer.Elapsed += new ElapsedEventHandler(acknowledgeDirectAddress);
                    acknowledgementTimer.Start();
                    break;

                case "request":
                    handleRequest(value);
                    break;

                case "dictation":
                    window.textBlock1.Text = (string)value.Value;
                    speechSynthesis.read((string)value.Value);
                    break;
            }

            foreach (KeyValuePair<string, SemanticValue> child in value)
            {
                parseSpeech(child);
            }
        }

        private void acknowledgeDirectAddress(object sender, ElapsedEventArgs e)
        {
            acknowledgementTimer.Stop();
            speechSynthesis.read("Yes?");
        }

        private void wakeSara()
        {
            if (isSaraAsleep)
            {
                isSaraAsleep = false;
                window.ActivateIndicator();
            }

            lastActivity = DateTime.Now;
        }

        private void monitorSara(object sender, EventArgs e)
        {
            double elapsedSeconds = DateTime.Now.Subtract(lastActivity).TotalSeconds;
            if (elapsedSeconds > timeout)
            {
                sleepSara();
            }

            if (alarmSet)
            {
                if (DateTime.Now.CompareTo(alarmTime) >= 0)
                {
                    speechSynthesis.readAlarmActive();
                    alarmSet = false;
                    snoozeActive = true;
                    snoozeTime = DateTime.Now.AddMinutes(4.5);
                }
            }

            if (snoozeActive)
            {
                if (DateTime.Now.CompareTo(snoozeTime) >= 0)
                {
                    speechSynthesis.readAlarmActive();
                    snoozeTime = DateTime.Now.AddMinutes(4.5);
                }
            }
        }

        private void sleepSara()
        {
            if (!isSaraAsleep)
            {
                isSaraAsleep = true;
                window.DeactivateIndicator();
                window.textBlock1.Text = "";
            }
        }

        private void handleRequest(SemanticValue request)
        {
            DateTime date = DateTime.Now;
            SemanticValue requestDetails = request;
            string requestType = (string)request.Value;


            foreach (KeyValuePair<string, SemanticValue> semantic in request)
            {
                switch (semantic.Key)
                {
                    case "date":
                        date = DateTime.Parse((string)semantic.Value.Value);
                        break;

                    case "time":
                        requestDetails = semantic.Value;
                        break;
                    
                    default:
                        break;
                }
            }

            switch ((string)request.Value)
            {
                case "weather":
                    WeatherModule weather = new WeatherModule();
                    window.textBlock1.Text = "You asked for the weather forecast for " + date.ToShortDateString();
                    speechSynthesis.readWeather(date);
                    break;

                case "meeting":
                    window.textBlock1.Text = "You wanted to know if you had any meetings scheduled for " + date.ToShortDateString();
                    speechSynthesis.readMeetings(date);
                    break;

                case "setAlarm":
                    setAlarm(requestDetails);
                    break;

                case "unsetAlarm":
                    unsetAlarm();
                    break;

                case "snooze":
                    snoozeAlarm();
                    break;

                case "whatTime":
                    speechSynthesis.readTime(DateTime.Now);
                    break;

                case "thankYou":
                    speechSynthesis.readYoureWelcome();
                    break;

                case "goToSleep":
                    speechSynthesis.readGoodnight();
                    sleepSara();
                    break;

                default:
                    break;
            }
        }

        public void setAlarm(SemanticValue time)
        {
            string Hour = "0";
            string Minute = "00";
            string Suffix = "AM";

            foreach (KeyValuePair<string, SemanticValue> semantic in time)
            {
                switch (semantic.Key)
                {
                    case "Hour":
                        Hour = (string)semantic.Value.Value;
                        break;

                    case "Minute":
                        if (((string)semantic.Value.Value).Equals("o clock"))
                        {
                            Minute = "00";
                        }
                        else
                        {
                            Minute = (string)semantic.Value.Value;
                        }
                        break;

                    case "Suffix":
                        Suffix = (string)semantic.Value.Value;
                        break;

                    default:
                        break;
                }
            }

            string TimeString = Hour + ":" + Minute + Suffix;

            alarmTime = DateTime.Parse(TimeString);

            if (DateTime.Now.CompareTo(alarmTime) > 0)
            {
                alarmTime = alarmTime.AddDays(1);
            }

            alarmSet = true;

            speechSynthesis.readAlarmSet(alarmTime);
        }

        public void unsetAlarm()
        {
            if (alarmSet || snoozeActive)
            {
                alarmSet = false;
                snoozeActive = false;
                speechSynthesis.stopReading();
                speechSynthesis.readAlarmUnset();
            }
        }

        public void snoozeAlarm()
        {
            if (alarmSet)
            {
                alarmSet = false;
                snoozeActive= true;
                snoozeTime = DateTime.Now.AddMinutes(4.5);
                speechSynthesis.stopReading();
                speechSynthesis.readSnooze(snoozeTime);
            }
        }

        public void SpeechEngine_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            //window.textBlock1.Text = "Rejected: \"" + e.Result.Text + "\"(" + e.Result.Confidence + ")\n";
        }

        public void SpeechEngine_RecognizeCompleted(object sender, RecognizeCompletedEventArgs e)
        {
            window.textBlock1.Text = "No longer listening.";
        }

        static private Grammar SaraGrammarBuilder(System.Globalization.CultureInfo culture)
        {
            DateTime today = DateTime.Now;
            DateTime tomorrow = DateTime.Now.AddDays(1);

            GrammarBuilder freeDictation = new GrammarBuilder();
            freeDictation.AppendDictation();

            GrammarBuilder date = new GrammarBuilder();
            Choices datesChoices = new Choices();
            datesChoices.Add(new GrammarBuilder(new SemanticResultValue("today", today.ToShortDateString())),
                             new GrammarBuilder(new SemanticResultValue("tomorrow", tomorrow.ToShortDateString())));

            date.Append(datesChoices);

            GrammarBuilder weather = new GrammarBuilder();
            Choices weatherChoices = new Choices("how's the weather",
                                                 "what's the weather like");
            weather.Append(weatherChoices);
            weather.Append(new GrammarBuilder(new SemanticResultKey("date", date)), 0, 1);
            weather.Append(new SemanticResultValue("weather"));

            GrammarBuilder meeting = new GrammarBuilder();
            Choices meetingChoices = new Choices("do I have any meetings",
                                                 "any meetings",
                                                 "what's my schedule",
                                                 "what's my schedule for");
            meeting.Append(meetingChoices);
            meeting.Append(new GrammarBuilder(new SemanticResultKey("date", date)), 0, 1);
            meeting.Append(new SemanticResultValue("meeting"));

            GrammarBuilder setAlarm = new GrammarBuilder();
            Choices setAlarmChoices = new Choices("set alarm for",
                                                  "set my alarm for",
                                                  "set the alarm for",
                                                  "set an alarm for",
                                                  "wake me up at",
                                                  "wake me at",
                                                  "I want to get up at");
            setAlarm.Append(setAlarmChoices);
            setAlarm.Append(new SemanticResultKey("time", Grammars.TimeGrammarBuilder()));
            setAlarm.Append(new SemanticResultValue("setAlarm"));

            GrammarBuilder unsetAlarm = new GrammarBuilder();
            Choices unsetAlarmChoices = new Choices("turn the alarm off",
                                                    "turn off the alarm",
                                                    "shut the alarm off",
                                                    "shut off the alarm",
                                                    "alarm off",
                                                    "turn that off",
                                                    "unset the alarm");
            unsetAlarm.Append(unsetAlarmChoices);
            unsetAlarm.Append(new SemanticResultValue("unsetAlarm"));

            GrammarBuilder snooze = new GrammarBuilder();
            Choices snoozeChoices = new Choices("snooze",
                                                "snooze alarm",
                                                "a few more minutes",
                                                "give me a few more minutes");
            snooze.Append(snoozeChoices);
            snooze.Append(new SemanticResultValue("snooze"));

            GrammarBuilder whatTime = new GrammarBuilder();
            Choices whatTimeChoices = new Choices("what time is it?",
                                                  "what's the time?",
                                                  "how late is it?");
            whatTime.Append(whatTimeChoices);
            whatTime.Append(new SemanticResultValue("whatTime"));

            GrammarBuilder thankYou = new GrammarBuilder();
            Choices thankYouChoices = new Choices("thanks",
                                                  "thank you");
            thankYou.Append(thankYouChoices);
            thankYou.Append(new SemanticResultValue("thankYou"));

            GrammarBuilder goToSleep = new GrammarBuilder();
            Choices goToSleepChoices = new Choices("go to sleep",
                                                   "sleep",
                                                   "goodnight",
                                                   "go away");
            goToSleep.Append(goToSleepChoices);
            goToSleep.Append(new SemanticResultValue("goToSleep"));

 

            GrammarBuilder requestType = new GrammarBuilder(new Choices(weather, meeting, 
                setAlarm, unsetAlarm, snooze, whatTime, thankYou, goToSleep));

            GrammarBuilder dictation = new GrammarBuilder();
            dictation.AppendDictation();

            GrammarBuilder requestGrammar = new GrammarBuilder();
            requestGrammar.Culture = culture;

            requestGrammar.Append(new Choices(new SemanticResultKey("directAddress", "Sara"),
                new SemanticResultKey("request", requestType))); 
                //new SemanticResultKey("dictation", dictation)));

            return new Grammar(requestGrammar);
        }
    }
}
